home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume91 / news / barn_201 / part02 / arn.c next >
C/C++ Source or Header  |  1991-02-07  |  44KB  |  1,514 lines

  1. /*
  2.  *    File Name:        arn.c
  3.  *    Project:        BARN - Bah's Amiga ReadNews.
  4.  *    Purpose:        Mainline code for Amiga News Reader.
  5.  *    Functions:        main.
  6.  *    Author:            Jeff Van Epps
  7.  *    Created:        02 Sep 89
  8.  *    Last Modified:    15 Jan 91
  9.  *    Comments:
  10.  *        barn [config_file]
  11.  *
  12.  *        config_file is optional, defaulting to "barn.config".
  13.  *
  14.  *    History:
  15.  *        02 Sep 89/JVE    Created.
  16.  *        03 Dec 89/JVE    Tries to cope with "Re: $x" subject lines when finding
  17.  *                        next article to read and when killing articles.
  18.  *        16 Dec 89/JVE    Fixed a bug in the last mark in UpdateReadList if the
  19.  *                        last article was still unread.
  20.  *        30 Dec 89/JVE    Implemented kill-list checking.
  21.  *        05 Sep 90/JVE    Removed volume references -- run from base directory
  22.  *                        of news articles.
  23.  *        12 Sep 90/JVE    Added parm to Paginate() to account for headers
  24.  *                        already printed.
  25.  *        17 Oct 90/JVE    Added '-' option to go back to previous article.
  26.  *                        Made Paginate() pass up the 'K' option.
  27.  *                        Added help option.  Added Reply and Followup.
  28.  *        19 Oct 90/JVE    UNIX (Sun) port.  Curses, readdir, etc.
  29.  *        20 Oct 90/JVE    Added configuration file stuff.
  30.  *        22 Oct 90/JVE    Use regular expressions for subject following and
  31.  *                        killing.
  32.  *        24 Oct 90/JVE    Added scan subjects command "=".
  33.  *        05 Nov 90/JVE    New opendir() and stat() functions from SAS/C used.
  34.  *        10 Nov 90/JVE    Make sure articles shown in order of posting.
  35.  *        11 Nov 90/JVE    Added LINES/COLS to config file.
  36.  *        14 Nov 90/JVE    Added "m" - mark as unread, and use of "n" (next)
  37.  *                        within pager.  Added CR/NL to print one more line
  38.  *                        in pager, scanner.
  39.  *        16 Nov 90/JVE    Scanner fix -- used to always show first article in
  40.  *                        group even if already read.
  41.  *        22 Nov 90/JVE    End page at form feed.
  42.  *        25 Nov 90/JVE    Config option to clear screen for each new page.
  43.  *        28 Nov 90/JVE    Fix to MakePattern to escape backslash in regexp.
  44.  *                        Changed getcmd() to take array of strings for "help"
  45.  *                        to escape compiler string length limitation.  Changed
  46.  *                        MakePattern() to allow characters between 'Re:' and
  47.  *                        'text' being matched.
  48.  *        05 Jan 91/JVE    Fixed line-wrap calculations in Paginate().  Put last
  49.  *                        line of previous page at top of next page when in
  50.  *                        NO_SCROLL mode.  Put 'subject' in article_info as an
  51.  *                        an optimization, so header list didn't have to be
  52.  *                        searched constantly.  Added '#' command to go to
  53.  *                        specific article by number.
  54.  *        06 Jan 91/JVE    Added back-page capability in Paginate().  Changed
  55.  *                        name to "barn" to avoid conflict with another "arn"
  56.  *                        in the Amiga world which performs the same function.
  57.  *                        Changed getcmd() to accept one list of commands which
  58.  *                        gets displayed to the user and one which doesn't.
  59.  *                        Added "a - About BARN" command.
  60.  *        15 Jan 91/JVE    Avoid regexec() on NULL subject.
  61.  */
  62.  
  63. # include    <stdio.h>
  64. # include    <stdlib.h>
  65. # include    <string.h>
  66. # include    <regexp.h>
  67.  
  68. # ifdef sun
  69. # include    <dirent.h>    /* opendir & friends */
  70. # else  /* not sun */
  71. # include    <sys/types.h>
  72. # include    <sys/dir.h>
  73. # endif /* sun */
  74.  
  75. # include    <sys/stat.h>
  76.  
  77. # include    "standard.h"
  78. # include    "article.h"
  79. # include    "ng.h"
  80. # include    "kill.h"
  81. # include    "configure.h"
  82. # include    "variables.h"
  83. # include    "reply.h"
  84. # include    "screenstuff.h"
  85.  
  86. # define    BARN_VERSION    "BARN version 2.01"
  87.  
  88. # define    DEFAULT_CONFIG    "barn.config"    /* ARN default configuration file */
  89. # define    DEFAULT_NEWSRC    ".newsrc"        /* default .newsrc filename */
  90. # define    DEFAULT_KILL    "KILL"            /* default name of kill files */
  91. # define    KILL            "KILL"            /* KILL filenames start with this */
  92. # define    REPLY_PATTERN    "^[RrEe: ]*"        /* regex for reply articles */
  93. # define    REPLY_REMOVER    "^[RrEe: ]*(.*)$"    /* regex for removing Re: */
  94.  
  95. # ifdef sun
  96.  
  97. /* LINES and COLS are variables set by curses. */
  98.  
  99. # define    Lines        LINES
  100. # define    Columns        COLS
  101.  
  102. # endif /* sun */
  103.  
  104. # ifdef sun
  105.  
  106. # define    COPY_CMD        "cp"
  107. # define    raw( x )        cbreak()
  108. # define    cooked( x )        nocbreak()
  109. # define    printf            printw
  110. # define    gets( x )        getstr( (x) )
  111. # define    getchar()        getch()
  112.  
  113. # else  /* not sun */
  114.  
  115. # define    COPY_CMD        "copy"
  116.  
  117. # endif /* sun */
  118.  
  119. typedef struct {
  120.     long            num;        /* article number */
  121.     ARTICLE_INFO    *pointer;    /* article structure */
  122.     } SORT_INFO;
  123.  
  124. static char            *currentdir;    /* current directory on startup */
  125.  
  126. # ifndef sun
  127.  
  128. static int            Lines, Columns;    /* terminal characteristics */
  129.  
  130. # endif
  131.  
  132. /*
  133.  *    Local function which creates the string form and internal form of a
  134.  *    regular expression.  The string form should have all regexp-special
  135.  *    characters escaped.
  136.  */
  137.  
  138. struct regexp        *MakePattern( char *pattern, char *original );
  139.  
  140. void                ScanSubjects( ARTICLE_INFO *arts );
  141.  
  142.  
  143. void main( argc, argv )
  144.  
  145. int        argc;
  146. char    **argv;
  147.  
  148. {
  149. NG_INFO            *alreadyread;    /* list of newsgroups/articles already read */
  150. NG_INFO            *ng;            /* ptr for newsgroup list traversal */
  151. KILL_INFO        *globalkill;    /* entries from global KILL file */
  152. char            *config_file;    /* name of configuration file to use */
  153. char            *newsrc_file;    /* name of .newsrc file */
  154. char            *global_kill_file;    /* name of global kill file */
  155. void            Startup(), CheckConfiguration();
  156. extern char        *getcwd();
  157.  
  158. currentdir = getcwd( NULLP( char ), BUFSIZ );
  159. if ( argc < 2 )
  160.     config_file = DEFAULT_CONFIG;
  161. else
  162.     config_file = argv[1];
  163. Configure( config_file );
  164. CheckConfiguration();
  165. Startup();
  166.  
  167. /*
  168.  *    Find all articles we've already read from the .newsrc file.
  169.  */
  170.  
  171. if ( ( newsrc_file = GetVar( VAR_NEWSRC ) ) == NULL )
  172.     newsrc_file = DEFAULT_NEWSRC;
  173. alreadyread = GetNewsRC( newsrc_file );
  174. if ( ( global_kill_file = GetVar( VAR_KILL ) ) == NULL )
  175.     global_kill_file = DEFAULT_KILL;
  176. globalkill = GetKillList( global_kill_file );
  177.  
  178. /*
  179.  *    For each newsgroup in the .newsrc, handle the newsgroup.
  180.  *    (Code needs to be added to notice newsgroups not in .newsrc)
  181.  */
  182.  
  183. for ( ng = alreadyread; ng != NULLP( NG_INFO ); ng = ng->next )
  184.     {
  185.     chdir( currentdir );
  186.     if ( ReadNewsgroup( ng, &globalkill ) )
  187.         break;
  188.     }
  189.  
  190. chdir( currentdir );
  191. PutNewsRC( newsrc_file, alreadyread );
  192. PutKillList( global_kill_file, globalkill );
  193.  
  194. DestroyNGList( alreadyread );
  195. DestroyKillList( globalkill );
  196.  
  197. cooked( stdin );
  198.  
  199. # ifdef sun
  200. endwin();
  201. # endif
  202. }
  203.  
  204.  
  205. /****************************************************************************/
  206. /*    FUNCTION:    AboutBARN                                                    */
  207. /*                                                                            */
  208. /*    PURPOSE:    Print version of BARN.                                        */
  209. /*                                                                            */
  210. /*    INPUT PARAMETERS:                                                        */
  211. /*        NAME        I/O        DESCRIPTION                                        */
  212. /*        ----        ---        -----------                                        */
  213. /*                                                                            */
  214. /*    RETURNS: none                                                            */
  215. /*                                                                            */
  216. /*    COMMENTS:                                                                */
  217. /*                                                                            */
  218. /*    HISTORY:                                                                */
  219. /*        1.    06 Jan 91        Created.                                        */
  220. /*                                                                            */
  221. /****************************************************************************/
  222.  
  223. void AboutBARN()
  224.  
  225. {
  226. printf( "\n\n%s written by Jeff Van Epps (aka Lord Bah)\n\n", BARN_VERSION );
  227. printf( "raw.c written by Chuck McManis\n" );
  228. printf( "sendpacket.c written by Phil Lindsay, Carolyn Scheppner, and Andy Finkel\n" );
  229. printf( "Uses regexp library written by Henry Spencer, copyrighted by the\n" );
  230. printf( "University of Toronto in 1986.\n" );
  231. printf( "\nExecutable freely redistributable with the restriction that\n" );
  232. printf( "the authors are not responsible for consequences of use.\n" );
  233. printf( "\nAddress comments to amusing!jeffv@bisco.kodak.com or lordbah@cup.portal.com.\n" );
  234. printf( "\nHit any key to continue" );
  235. (void) getchar();
  236. printf( "\n" );
  237. }
  238.  
  239.  
  240. /****************************************************************************/
  241. /*    FUNCTION:    Startup                                                        */
  242. /*                                                                            */
  243. /*    PURPOSE:    Get ready to run ARN.                                        */
  244. /*                                                                            */
  245. /*    INPUT PARAMETERS:                                                        */
  246. /*        NAME        I/O        DESCRIPTION                                        */
  247. /*        ----        ---        -----------                                        */
  248. /*                                                                            */
  249. /*    RETURNS:                                                                */
  250. /*                                                                            */
  251. /*    COMMENTS:                                                                */
  252. /*                                                                            */
  253. /*    HISTORY:                                                                */
  254. /*        1.    05 Sep 89        Created.                                        */
  255. /*                                                                            */
  256. /****************************************************************************/
  257.  
  258. void Startup()
  259.  
  260. {
  261. # ifdef sun
  262. (void) initscr();
  263. # endif
  264. raw( stdin );
  265. }
  266.  
  267.  
  268. /****************************************************************************/
  269. /*    FUNCTION:    CheckConfiguration                                            */
  270. /*                                                                            */
  271. /*    PURPOSE:    Check to see that user has defined required variables in    */
  272. /*                the configuration file.                                        */
  273. /*                                                                            */
  274. /*    INPUT PARAMETERS:                                                        */
  275. /*        NAME        I/O        DESCRIPTION                                        */
  276. /*        ----        ---        -----------                                        */
  277. /*                                                                            */
  278. /*    RETURNS: none                                                            */
  279. /*                                                                            */
  280. /*    COMMENTS:                                                                */
  281. /*        Exits if any of the required variables are not set.                    */
  282. /*                                                                            */
  283. /*    HISTORY:                                                                */
  284. /*        1.    20 Oct 90        Created.                                        */
  285. /*        2.    11 Nov 90        Added LINES & COLS.                                */
  286. /*                                                                            */
  287. /****************************************************************************/
  288.  
  289. static void CheckConfiguration()
  290.  
  291. {
  292. char        *tmp;
  293.  
  294. if ( GetVar( VAR_USER ) == NULL || GetVar( VAR_NODE ) == NULL ||
  295.   GetVar( VAR_DOMAIN ) == NULL || GetVar( VAR_NAME ) == NULL )
  296.       {
  297.       fprintf( stderr, "Must define \"%s\", \"%s\", \"%s\", and \"%s\" in configuration file.\n",
  298.         VAR_USER, VAR_NODE, VAR_DOMAIN, VAR_NAME );
  299.       exit( 1 );
  300.       }
  301.  
  302. # ifndef sun
  303.  
  304. if ( ( ( tmp = GetVar( VAR_LINES ) ) == NULL ) || 
  305.   ( Lines = atoi( tmp ) ) < 10 ||
  306.   Lines > 48 )
  307.       {
  308.       fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n",
  309.         VAR_LINES );
  310.       exit( 1 );
  311.       }
  312. if ( ( ( tmp = GetVar( VAR_COLS ) ) == NULL ) || 
  313.   ( Columns = atoi( tmp ) ) < 10 ||
  314.   Columns > 133 )
  315.       {
  316.       fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n",
  317.         VAR_COLS );
  318.       exit( 1 );
  319.       }
  320.  
  321. # endif
  322.  
  323. }
  324.  
  325.  
  326. /****************************************************************************/
  327. /*    FUNCTION:    ReadNewsgroup                                                */
  328. /*                                                                            */
  329. /*    PURPOSE:    Allow user to read articles within a newsgroup.                */
  330. /*                                                                            */
  331. /*    INPUT PARAMETERS:                                                        */
  332. /*        NAME        I/O        DESCRIPTION                                        */
  333. /*        ----        ---        -----------                                        */
  334. /*        ng            I/O        List of articles read within newsgroup.            */
  335. /*        gkill        I/O        Global kill list.                                */
  336. /*                                                                            */
  337. /*    RETURNS:                                                                */
  338. /*        TRUE                If user wants to quit.                            */
  339. /*        FALSE                Otherwise.                                        */
  340. /*                                                                            */
  341. /*    COMMENTS:                                                                */
  342. /*                                                                            */
  343. /*    HISTORY:                                                                */
  344. /*        1.    04 Sep 89        Created.                                        */
  345. /*        2.    06 Jan 91        Added "a" - About BARN command.                    */
  346. /*                                                                            */
  347. /****************************************************************************/
  348.  
  349. # define    READ_CMDS    "acnqy "
  350. static char *READ_HELP[] = {
  351. "<space>  Yes, read this newsgroup.",
  352. "   a     About BARN.",
  353. "   c     Catch up.  Mark everything in this newsgroup as read.",
  354. "   n     No, don't read this newsgroup.",
  355. "   q     Quit ARN.",
  356. "   y     Yes, read this newsgroup.",
  357. (char *) NULL
  358. };
  359.  
  360. ReadNewsgroup( ng, gkill )
  361.  
  362. NG_INFO        *ng;
  363. KILL_INFO    **gkill;
  364.  
  365. {
  366. KILL_INFO        *lkill;        /* local (within newsgroup) kill list */
  367. DIR                *dir;        /* pointer to directory structure */
  368. # ifdef sun
  369. struct dirent    *info;        /* pointer to each file's info */
  370. # else  /* not sun, Amiga */
  371. struct direct    *info;
  372. # endif /* sun */
  373. struct stat        stats;        /* file system information about file */
  374. ARTICLE_INFO    *todo;        /* articles to be read */
  375. ARTICLE_INFO    *art;        /* info on eligible article */
  376. ARTICLE_INFO    **where;    /* where to link in next eligible article */
  377. ARTICLE_INFO    *tmp;
  378. char            *fn;        /* name of file currently being checked */
  379. int                narticles;    /* number of unread articles in newsgroup */
  380. char            *kill_file;    /* name of local kill file */
  381. ARTICLE_INFO    *CheckArticle();
  382. int                rc = FALSE, done = FALSE;
  383. void            ReadArticles(), SortArticles(), UpdateReadList();
  384.  
  385. if ( chdir( ng->name ) != 0 )
  386.     fprintf( stderr, "Newsgroup directory %s missing!\n", ng->name );
  387. else
  388.     {
  389.     printf( "Checking %s ...\n", ng -> name );
  390.     if ( ( kill_file = GetVar( VAR_KILL ) ) == NULL )
  391.         kill_file = DEFAULT_KILL;
  392.     lkill = GetKillList( kill_file );
  393.     /*
  394.      *    For each file in directory, check vs. already read (ng), check
  395.      *    vs. global kill (gkill), check vs. local kill (lkill). If killed,
  396.      *    add to already read (ng). If not read and not killed, put on
  397.      *    unread list (todo).
  398.      */
  399. # ifdef sun
  400.     if ( ( ( dir = opendir( "." ) ) == NULL ) || 
  401. # else  /* Amiga */
  402.     if ( ( ( dir = opendir( "" ) ) == NULL ) || 
  403. # endif
  404.       ( info = readdir( dir ) ) == NULL )
  405.         printf( "%s is empty.\n", ng->name );
  406.     else
  407.         {
  408.         todo = NULLP( ARTICLE_INFO );
  409.         where = &todo;
  410.         narticles = 0;
  411.         do {
  412.             /*
  413.              *    If filename starts with "KILL", this is a KILL file, not a
  414.              *    news article.  Don't open anything but regular files.
  415.              */
  416.             if ( strncmp( fn = info -> d_name, KILL, strlen(KILL) ) != 0 &&
  417.               stat( fn, &stats ) == 0 && ( stats.st_mode & S_IFREG ) )
  418.                 if ( art = CheckArticle( fn, ng->markers, *gkill, lkill ) )
  419.                     {
  420.                     *where = art;
  421.                     where = & (*where)->next;
  422.                     ++narticles;
  423.                     }
  424.             } while ( ( info = readdir( dir ) ) != NULL );
  425.         closedir( dir );
  426.         /* talk to user */
  427.         if ( narticles == 0 )
  428.             printf( "%s has no unread articles.\n", ng->name );
  429.         else while ( !done )
  430.             {
  431.             printf( "%s has %d unread articles. Read now? ", ng->name, narticles );
  432.             switch ( getcmd( READ_CMDS, READ_HELP, "" ) ) {
  433.                 case ' ':
  434.                 case 'y':
  435.                     SortArticles( &todo, narticles );
  436.                     for(art = todo; art != NULLP(ARTICLE_INFO); art = art->next)
  437.                         if ( art->beenread )
  438.                             --narticles;
  439.                     ReadArticles( ng, todo, narticles, gkill, &lkill );
  440.                     printf( "\n" );
  441.                     UpdateReadList( ng, todo );
  442.                     done = TRUE;
  443.                     break;
  444.                 case 'q':
  445.                     rc = TRUE;
  446.                     printf( "\n" );
  447.                     done = TRUE;
  448.                     break;
  449.                 case 'c':    /* catch up by marking all articles read */
  450.                     printf( "\n" );
  451.                     for ( tmp = todo; tmp != NULLP( ARTICLE_INFO ); tmp = tmp->next )
  452.                         tmp->beenread = TRUE;
  453.                     UpdateReadList( ng, todo );
  454.                     done = TRUE;
  455.                     break;
  456.                 case 'a':
  457.                     AboutBARN();
  458.                     break;
  459.                 case 'n':    /* don't read now, just goto cleanup below */
  460.                     printf( "\n" );
  461.                     done = TRUE;
  462.                     break;
  463.                 default:
  464.                     break;
  465.                 }
  466.             }
  467.         /* kill any remaining todo list */
  468.         for ( ; todo != NULLP( ARTICLE_INFO ); todo = tmp )
  469.             {
  470.             tmp = todo->next;
  471.             DestroyArticle( todo );
  472.             }
  473.         }
  474.     /*
  475.      *    Write the local kill file to disk in case we changed it.
  476.      */
  477.     PutKillList( kill_file, lkill );
  478.     DestroyKillList( lkill );
  479.     }
  480. return rc;
  481. }
  482.  
  483.  
  484. /****************************************************************************/
  485. /*    FUNCTION:    CheckArticle                                                */
  486. /*                                                                            */
  487. /*    PURPOSE:    Check readability of an article.                            */
  488. /*                                                                            */
  489. /*    INPUT PARAMETERS:                                                        */
  490. /*        NAME        I/O        DESCRIPTION                                        */
  491. /*        ----        ---        -----------                                        */
  492. /*        filename     I        Name of file containing article to be checked.    */
  493. /*        markers         I        Pointer to list of articles already read.        */
  494. /*        gkill         I        Global kill list.                                */
  495. /*        lkill         I        Local kill list.                                */
  496. /*                                                                            */
  497. /*    RETURNS:                                                                */
  498. /*        (ARTICLE_INFO *)    Pointer to article, if readable.                */
  499. /*        (NULL)                If not readable.                                */
  500. /*                                                                            */
  501. /*    COMMENTS:                                                                */
  502. /*                                                                            */
  503. /*    HISTORY:                                                                */
  504. /*        1.    04 Sep 89        Created.                                        */
  505. /*        2.    30 Dec 89        Implemented kill-list checking.                    */
  506. /*        3.    22 Oct 90        Changed from string compare to regular             */
  507. /*                            expression match.                                */
  508. /*        4.    24 Oct 90        Tells what it killed and why.                    */
  509. /*                                                                            */
  510. /****************************************************************************/
  511.  
  512. ARTICLE_INFO    *CheckArticle( filename, markers, gkill, lkill )
  513.  
  514. char        *filename;
  515. MARKER        *markers;
  516. KILL_INFO    *gkill, *lkill;
  517.  
  518. {
  519. long            article_number;
  520. ARTICLE_INFO    *art;
  521. HEADER_INFO        *hdr;
  522. KILL_INFO        *kp;
  523. int                done = FALSE;
  524.  
  525. article_number = atol( filename );
  526. if ( NumberCovered( markers, article_number ) )
  527.     return NULLP( ARTICLE_INFO );
  528. if ( ( art = ParseArticle( filename ) ) == NULL )
  529.     return art;
  530.  
  531. /*
  532.  *    Check kill lists.
  533.  */
  534.  
  535. for ( hdr = art->headers; hdr != NULLP(HEADER_INFO) && !done; hdr = hdr->next )
  536.     {
  537.     for ( kp = gkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next )
  538.         if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 &&
  539.              regexec( kp -> pattern, hdr -> fieldvalue ) == 1 )
  540.                  {
  541.                  done = art->beenread = TRUE;
  542.                  printf( "Killed %ld, %s: %s\n", art -> number,
  543.                    hdr -> fieldname, hdr -> fieldvalue );
  544.                  }
  545.     for ( kp = lkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next )
  546.         if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 &&
  547.              regexec( kp -> pattern, hdr -> fieldvalue ) == 1 )
  548.                  {
  549.                  done = art->beenread = TRUE;
  550.                  printf( "Killed %ld, %s: %s\n", art -> number,
  551.                    hdr -> fieldname, hdr -> fieldvalue );
  552.                  }
  553.     }
  554. return art;
  555. }
  556.  
  557.  
  558. /****************************************************************************/
  559. /*    FUNCTION:    FindNextArticle                                                */
  560. /*                                                                            */
  561. /*    PURPOSE:    Find next article on same subject thread.                    */
  562. /*                                                                            */
  563. /*    INPUT PARAMETERS:                                                        */
  564. /*        NAME        I/O        DESCRIPTION                                        */
  565. /*        ----        ---        -----------                                        */
  566. /*        current         I        Last article displayed.                            */
  567. /*        sub             I        Subject line of last article.                    */
  568. /*        found         O        Indicate whether or not we found another art.    */
  569. /*        top             I        Head of list of articles.                        */
  570. /*                                                                            */
  571. /*    RETURNS:                                                                */
  572. /*        (ARTICLE_INFO *)    next article on subject thread.                    */
  573. /*                                                                            */
  574. /*    COMMENTS:                                                                */
  575. /*        Returns "current" if no match is found.                                */
  576. /*                                                                            */
  577. /*    HISTORY:                                                                */
  578. /*        1.    14 Nov 90        Created.                                        */
  579. /*                                                                            */
  580. /****************************************************************************/
  581.  
  582. ARTICLE_INFO    *FindNextArticle( current, sub, found, top )
  583.  
  584. ARTICLE_INFO    *current;
  585. char            *sub;
  586. int                *found;
  587. ARTICLE_INFO    *top;
  588.  
  589. {
  590. ARTICLE_INFO        *p;
  591. char                temp[MAXLINE];
  592. struct regexp        *subre;        /* regular expression pattern for subj match */
  593.  
  594. *found = FALSE;
  595. if ( ( subre = MakePattern( temp, sub ) ) == NULL )
  596.     fprintf( stderr, "Can't compile regex '%s'\n", temp );
  597. for ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; )
  598.     {
  599.     if ( ! p->beenread )
  600.         if ( p -> subject )
  601.             if ( regexec( subre, p -> subject ) == 1 )
  602.                 {
  603.                 current = p;
  604.                 *found = TRUE;
  605.                 }
  606.     if ( *found )
  607.         break;
  608.     if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) )
  609.         p = top;
  610.     }
  611. if ( subre != NULL )
  612.     free( (char *) subre );
  613. return p;
  614. }
  615.  
  616. /****************************************************************************/
  617. /*    FUNCTION:    ReadArticles                                                */
  618. /*                                                                            */
  619. /*    PURPOSE:    Allow user to read articles within a newsgroup.                */
  620. /*                                                                            */
  621. /*    INPUT PARAMETERS:                                                        */
  622. /*        NAME        I/O        DESCRIPTION                                        */
  623. /*        ----        ---        -----------                                        */
  624. /*        ng            I/O        List of articles read.                            */
  625. /*        todo        I/O        List of articles to be read.                    */
  626. /*        narticles     I        Number of unread articles.                        */
  627. /*        gkill        I/O        Global kill list.                                */
  628. /*        lkill        I/O        Local kill list.                                */
  629. /*                                                                            */
  630. /*    RETURNS:                                                                */
  631. /*                                                                            */
  632. /*    COMMENTS:                                                                */
  633. /*                                                                            */
  634. /*    HISTORY:                                                                */
  635. /*        1.    04 Sep 89        Created.                                        */
  636. /*        2.    17 Oct 90        Added '-' command to go to previous article.    */
  637. /*        3.    10 Nov 90        Find next art from beginning if not following    */
  638. /*                            Subject thread.  Makes sure articles get shown    */
  639. /*                            in order of posting.                            */
  640. /*        4.    28 Nov 90        Added "/" to let user specify Subject to match.    */
  641. /*        5.    05 Jan 91        Added "#" to go to specific article number.        */
  642. /*        6.    06 Jan 91        Added "a" command for About BARN information.    */
  643. /*                                                                            */
  644. /****************************************************************************/
  645.  
  646. # define    CMDS        "acFfjKkmnqRrwy-=/# "
  647. static char *CMDS_HELP[] = {
  648. "   a     About BARN.",
  649. "   c     Catch up.  Mark all articles in this newsgroup as read.",
  650. "   Ff    Followup, 'F' includes current article.",
  651. "   Kk    Kill articles with this Subject, 'K' adds to KILL file.",
  652. "   m     Mark this article as unread.",
  653. "   n,j   Mark this article as read.",
  654. "   q     Quit this newsgroup.",
  655. "   Rr    Reply via mail, 'R' includes current article.",
  656. "   w     Write this article.  Prompts for filename.",
  657. " y,<sp>  Read this article.",
  658. "   -     Go to previous article, marking it as unread.",
  659. "   =     Scan subjects.",
  660. "   /     Find next article with subject entered.",
  661. "   #     Go to numbered article.",
  662. (char *) NULL
  663. };
  664.  
  665. void    ReadArticles( ng, todo, narticles, gkill, lkill )
  666.  
  667. NG_INFO                *ng;
  668. ARTICLE_INFO        *todo;
  669. int                    narticles;
  670. KILL_INFO            **gkill, **lkill;
  671.  
  672. {
  673. int                    cmd;        /* user's command */
  674. ARTICLE_INFO        *current;    /* article currently being examined */
  675. ARTICLE_INFO        *previous = NULLP( ARTICLE_INFO );    /* last art. examined */
  676. ARTICLE_INFO        *last;        /* used to track previous */
  677. ARTICLE_INFO        *p;
  678. KILL_INFO            *tmp;
  679. char                temp[BUFSIZ], buf[BUFSIZ], *sub, followsub[BUFSIZ];
  680. int                    readit;        /* did article get read? */
  681. int                    found;        /* found another article with same subject? */
  682. int                    follow = FALSE;    /* following subject thread? */
  683. int                    lines;        /* lines used on screen for header */
  684. struct regexp        *subre;        /* regular expression pattern for subj match */
  685. void                DisplayHeaders();
  686. ARTICLE_INFO        *FindNumberedArticle();
  687.  
  688. current = todo;
  689. while ( narticles > 0 )
  690.     {
  691.     while ( current->beenread )
  692.         if ( ( current = current->next ) == NULLP( ARTICLE_INFO ) )
  693.             current = todo;
  694.     Clear_Screen;
  695.     printf( "Article %ld (%d more) in %s\n", current->number, narticles - 1, ng->name );
  696.     lines = 1;
  697.     DisplayHeaders( current, &lines, &sub );
  698.     printf( "\nRead? " );
  699.     ++lines;
  700.     readit = FALSE;
  701.     last = current;
  702.     if ( ( cmd = getcmd( CMDS, CMDS_HELP, "" ) ) == ' ' || cmd == 'y' )
  703.         {
  704.         cmd = Paginate( current, lines );
  705.         readit = TRUE;
  706.         }
  707.     found = FALSE;
  708.     switch ( cmd ) {
  709.         case ' ':
  710.         case 'y':
  711.             current->beenread = TRUE;
  712.             if ( --narticles == 0 )
  713.                 break;
  714.             /*
  715.              *    Find next article with same subject.
  716.              */
  717.             if ( !follow )
  718.                 strcpy( followsub, sub );
  719.             current = FindNextArticle( current, followsub, &found, todo );
  720.             follow = found;
  721.             break;
  722.         case 'a':
  723.             AboutBARN();
  724.             found = TRUE;    /* don't let us go to top of newsgroup */
  725.             break;
  726.         case '/':
  727.             cooked( stdin );
  728.             printf( "/" );
  729.             gets( buf );
  730.             current = FindNextArticle( current, buf, &found, todo );
  731.             if ( follow = found )
  732.                 strcpy( followsub, buf );
  733.             else
  734.                 printf( "Not found.\n" );
  735.             raw( stdin );
  736.             break;
  737.         case '#':
  738.             cooked( stdin );
  739.             printf( "#" );
  740.             gets( buf );
  741.             current = FindNumberedArticle( todo, (long) atol( buf ) );
  742.             if ( current == NULLP( ARTICLE_INFO ) )
  743.                 {
  744.                 printf( "Not found.\n" );
  745.                 current = last;
  746.                 }
  747.             else if ( current -> beenread )
  748.                 {
  749.                 current -> beenread = FALSE;
  750.                 ++narticles;
  751.                 }
  752.             raw( stdin );
  753.             found = TRUE;    /* so we don't start at top of newsgroup */
  754.             break;
  755.         case 'k':
  756.         case 'K':
  757.             /* kill */
  758.             current->beenread = TRUE;
  759.             --narticles;
  760.             printf( "\nKilling %s ...\n", sub );
  761.             if ( ( subre = MakePattern( temp, sub ) ) == NULL )
  762.                 fprintf( stderr, "Can't compile regex '%s'\n", temp );
  763.             for ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; )
  764.                 {
  765.                 if ( ! p->beenread )
  766.                     if ( p -> subject && regexec( subre, p -> subject ) == 1 )
  767.                         {
  768.                         p->beenread = TRUE;
  769.                         --narticles;
  770.                         }
  771.                 if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) )
  772.                     p = todo;
  773.                 }
  774.             if ( cmd == 'K' )
  775.                 {
  776.                 printf( "Adding to newsgroup KILL file..." );
  777.                 tmp = (KILL_INFO *) malloc( sizeof( KILL_INFO ) );
  778.                 tmp->fieldname = strdup( HDR_SUBJECT );
  779.                 tmp->fieldvalue = strdup( temp );
  780.                 tmp->pattern = subre;
  781.                 tmp->next = *lkill;
  782.                 *lkill = tmp;
  783.                 printf( "Done\n" );
  784.                 }
  785.             else if ( subre != NULL )
  786.                 free( (char *) subre );
  787.             follow = FALSE;
  788.             break;
  789.         case 'n':
  790.         case 'j':
  791.             /*
  792.              *    Mark this article as read and move on to the next one with
  793.              *    the same subject.
  794.              */
  795.             current->beenread = TRUE;
  796.             --narticles;
  797.             /* FALL THROUGH */
  798.         case 'm':
  799.             /*
  800.              *    "Mark as unread".  This simply goes on to the next unread
  801.              *    article without marking this one as read.  Better marking
  802.              *    routines are possible.
  803.              */
  804.             if ( !follow )
  805.                 strcpy( followsub, sub );
  806.             current = FindNextArticle( current, followsub, &found, todo );
  807.             follow = found;
  808.             if ( !found && cmd == 'm' )
  809.                 {
  810.                 if ( ( current = current -> next ) == NULLP( ARTICLE_INFO ) )
  811.                     current = todo;
  812.                 }
  813.             else if ( !found )
  814.                 current = todo;
  815.             break;
  816.         case 'q':
  817.             if ( readit )
  818.                 current->beenread = TRUE;
  819.             narticles = 0;        /* break out of while loop */
  820.             break;
  821.         case 'c':        /* catch up by marking all articles read */
  822.             for ( current = todo; narticles > 0; current = current->next )
  823.                 {
  824.                 if ( ! current->beenread )
  825.                     --narticles;
  826.                 current->beenread = TRUE;
  827.                 }
  828.             break;
  829.         case 'w':        /* write article to top directory */
  830.             if ( readit )
  831.                 {
  832.                 current->beenread = TRUE;
  833.                 --narticles;
  834.                 }
  835.             cooked( stdin );
  836.             printf( "\nFilename: " );
  837.             gets( buf );
  838.             raw( stdin );
  839.             sprintf( temp, "%s %ld %s/%s", COPY_CMD, current->number, 
  840.                 currentdir, buf );
  841.             system( temp );
  842.             break;
  843.         case 'r':
  844.             Reply( current, FALSE );
  845.             break;
  846.         case 'R':
  847.             Reply( current, TRUE );
  848.             break;
  849.         case 'f':
  850.             Followup( ng -> name, current, FALSE );
  851.             break;
  852.         case 'F':
  853.             Followup( ng -> name, current, TRUE );
  854.             break;
  855.         case '-':
  856.             if ( previous != NULL )
  857.                 {
  858.                 current = previous;
  859.                 if ( current -> beenread )
  860.                     {
  861.                     current -> beenread = FALSE;
  862.                     ++narticles;
  863.                     }
  864.                 }
  865.             break;
  866.         case '=':    /* subject scan */
  867.             ScanSubjects( todo );
  868.             break;
  869.         default:
  870.             break;
  871.         }
  872.     previous = last;
  873.     if ( !found )
  874.         current = todo;        /* if not following thread, start at top */
  875.     }
  876. }
  877.  
  878.  
  879. /****************************************************************************/
  880. /*    FUNCTION:    DisplayHeaders                                                */
  881. /*                                                                            */
  882. /*    PURPOSE:    Print interesting article headers.                            */
  883. /*                                                                            */
  884. /*    INPUT PARAMETERS:                                                        */
  885. /*        NAME        I/O        DESCRIPTION                                        */
  886. /*        ----        ---        -----------                                        */
  887. /*        current         I        Pointer to current article.                        */
  888. /*        lines         O        Pointer to count of lines printed on screen.    */
  889. /*        sub             O        Pointer to current subject pointer.                */
  890. /*                                                                            */
  891. /*    RETURNS: none                                                            */
  892. /*                                                                            */
  893. /*    COMMENTS:                                                                */
  894. /*        Prints all headers stored in the articles header list, keeping        */
  895. /*        track of the number of lines printed on the screen and returning    */
  896. /*        that in 'lines'.  When the 'Subject' header is encountered, a        */
  897. /*        pointer to its value is stored in 'sub'.                            */
  898. /*                                                                            */
  899. /*    HISTORY:                                                                */
  900. /*        1.    05 Jan 91        Factored out of ReadArticles().                    */
  901. /*                                                                            */
  902. /****************************************************************************/
  903.  
  904. void DisplayHeaders( current, lines, sub )
  905.  
  906. ARTICLE_INFO    *current;
  907. int                *lines;
  908. char            **sub;
  909.  
  910. {
  911. HEADER_INFO        *ptr;
  912.  
  913. for ( ptr = current->headers; ptr != NULLP( HEADER_INFO ); ptr = ptr->next )
  914.     {
  915.     ++*lines;
  916.     if ( strcmp( ptr->fieldname, HDR_SUBJECT ) == 0 )
  917.         {
  918.         *sub = ptr->fieldvalue;
  919.         printf( "%s: ", ptr -> fieldname );
  920.         UL_ON;
  921.         printf( "%s", *sub );
  922.         UL_OFF;
  923.         printf( "\n" );
  924.         if ( strlen( ptr->fieldname ) + strlen( *sub ) + 2 > Columns )
  925.             ++*lines;
  926.         }
  927.     else
  928.         {
  929.         printf( "%s: %s\n", ptr->fieldname, ptr->fieldvalue );
  930.         if ( strlen( ptr->fieldname ) + strlen( ptr->fieldvalue ) + 2 > Columns )
  931.             ++*lines;
  932.         }
  933.     }
  934. }
  935.  
  936.  
  937. /****************************************************************************/
  938. /*    FUNCTION:    FindNumberedArticle                                            */
  939. /*                                                                            */
  940. /*    PURPOSE:    Return pointer to article identified by given number.        */
  941. /*                                                                            */
  942. /*    INPUT PARAMETERS:                                                        */
  943. /*        NAME        I/O        DESCRIPTION                                        */
  944. /*        ----        ---        -----------                                        */
  945. /*        head         I        Pointer to head of article list.                */
  946. /*        number         I        Number of article to find.                        */
  947. /*                                                                            */
  948. /*    RETURNS:                                                                */
  949. /*        NULL                No such numbered article.                        */
  950. /*        ARTICLE_INFO *ptr    Pointer to numbered article.                    */
  951. /*                                                                            */
  952. /*    COMMENTS:                                                                */
  953. /*                                                                            */
  954. /*    HISTORY:                                                                */
  955. /*        1.    05 Jan 91        Created.                                        */
  956. /*                                                                            */
  957. /****************************************************************************/
  958.  
  959. ARTICLE_INFO    *FindNumberedArticle( head, number )
  960.  
  961. ARTICLE_INFO    *head;
  962. long            number;
  963.  
  964. {
  965. ARTICLE_INFO    *p;
  966.  
  967. for ( p = head; p != NULLP( ARTICLE_INFO ); p = p -> next )
  968.     if ( p -> number == number )
  969.         return p;
  970.     else if ( p -> number > number )
  971.         break;
  972. return NULLP( ARTICLE_INFO );
  973. }
  974.  
  975. /****************************************************************************/
  976. /*    FUNCTION:    SortArticles                                                */
  977. /*                                                                            */
  978. /*    PURPOSE:    Sort article list by article number.                        */
  979. /*                                                                            */
  980. /*    INPUT PARAMETERS:                                                        */
  981. /*        NAME        I/O        DESCRIPTION                                        */
  982. /*        ----        ---        -----------                                        */
  983. /*        arts        I/O        Pointer to list of articles.                    */
  984. /*        narticles     I        Number of articles in list.                        */
  985. /*                                                                            */
  986. /*    RETURNS: none                                                            */
  987. /*                                                                            */
  988. /*    COMMENTS:                                                                */
  989. /*        *arts is modified.                                                    */
  990. /*                                                                            */
  991. /*    HISTORY:                                                                */
  992. /*        1.    04 Sep 89        Created.                                        */
  993. /*                                                                            */
  994. /****************************************************************************/
  995.  
  996. void SortArticles( arts, narticles )
  997.  
  998. ARTICLE_INFO        **arts;
  999. int                    narticles;
  1000.  
  1001. {
  1002. ARTICLE_INFO        *ptr;
  1003. ARTICLE_INFO        **where;
  1004. SORT_INFO            *articles;
  1005. int                    i;
  1006. int                    Compare();
  1007.  
  1008. articles = (SORT_INFO *) malloc( sizeof( SORT_INFO ) * narticles );
  1009. for ( ptr = *arts, i = 0; i < narticles; i++, ptr = ptr->next )
  1010.     {
  1011.     articles[i].pointer = ptr;
  1012.     articles[i].num = ptr->number;
  1013.     }
  1014. qsort( (char *) articles, narticles, sizeof( SORT_INFO ), Compare );
  1015. where = arts;
  1016. for ( i = 0; i < narticles; i++ )
  1017.     {
  1018.     *where = articles[i].pointer;
  1019.     where = &(*where)->next;
  1020.     }
  1021. *where = NULLP( ARTICLE_INFO );
  1022. free( articles );
  1023. }
  1024.  
  1025.  
  1026. /****************************************************************************/
  1027. /*    FUNCTION:    Compare                                                        */
  1028. /*                                                                            */
  1029. /*    PURPOSE:    Help function to sort articles.                                */
  1030. /*                                                                            */
  1031. /*    INPUT PARAMETERS:                                                        */
  1032. /*        NAME        I/O        DESCRIPTION                                        */
  1033. /*        ----        ---        -----------                                        */
  1034. /*        as defined by qsort()                                                */
  1035. /*                                                                            */
  1036. /*    RETURNS:                                                                */
  1037. /*        as defined by qsort()                                                */
  1038. /*                                                                            */
  1039. /*    COMMENTS:                                                                */
  1040. /*                                                                            */
  1041. /*    HISTORY:                                                                */
  1042. /*        1.    04 Sep 89        Created.                                        */
  1043. /*                                                                            */
  1044. /****************************************************************************/
  1045.  
  1046. Compare( first, second, size )
  1047.  
  1048. SORT_INFO        *first, *second;
  1049. int                size;
  1050.  
  1051. {
  1052. if ( first->num < second->num )
  1053.     return -1;
  1054. else if ( first->num > second->num )
  1055.     return 1;
  1056. else
  1057.     return 0;
  1058. }
  1059.  
  1060.  
  1061. /****************************************************************************/
  1062. /*    FUNCTION:    getcmd                                                        */
  1063. /*                                                                            */
  1064. /*    PURPOSE:    Get a command from the user.                                */
  1065. /*                                                                            */
  1066. /*    INPUT PARAMETERS:                                                        */
  1067. /*        NAME        I/O        DESCRIPTION                                        */
  1068. /*        ----        ---        -----------                                        */
  1069. /*        cmdlist         I        List of valid commands.                            */
  1070. /*        help         I        Array of help strings to display for user.        */
  1071. /*        otherlist     I        List of other valid commands that aren't shown    */
  1072. /*                            to the user.                                    */
  1073. /*                                                                            */
  1074. /*    RETURNS:                                                                */
  1075. /*                                                                            */
  1076. /*    COMMENTS:                                                                */
  1077. /*        The commands string must not allow 'h', 'H', or '?' as these are    */
  1078. /*        used to view the help string.                                        */
  1079. /*                                                                            */
  1080. /*    HISTORY:                                                                */
  1081. /*        1.    04 Sep 89        Created.                                        */
  1082. /*        2.    17 Oct 90        Added help options.                                */
  1083. /*        3.    14 Nov 90        Added CR/NL.                                    */
  1084. /*        4.    28 Nov 90        Made "help" array of strings to escape compiler    */
  1085. /*                            string length limitations.                        */
  1086. /*        5.    06 Jan 91        Generalized special cases of CR/NL to otherlist.*/
  1087. /*                                                                            */
  1088. /****************************************************************************/
  1089.  
  1090. getcmd( cmdlist, help, otherlist )
  1091.  
  1092. char            *cmdlist;
  1093. char            **help;
  1094. char            *otherlist;
  1095.  
  1096. {
  1097. int                ch;
  1098. char            **p;
  1099.  
  1100. ch = '"';
  1101. printf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) );
  1102. while ( strchr( cmdlist, (char) ch ) == NULLP( char ) )
  1103.     {
  1104.     ch = getchar();
  1105.     if ( ch == 'h' || ch == 'H' || ch == '?' )    /* user wants help */
  1106.         {
  1107.         INVERSE_OFF;
  1108.         printf( "\n" );
  1109.         for ( p = help; *p != (char *) NULL; p++ )
  1110.             printf( "%s\n", *p );
  1111.         printf( "\n" );
  1112.         printf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) );
  1113.         }
  1114.     else if ( strchr( otherlist, (char) ch ) != NULLP( char ) )
  1115.         break;
  1116.     }
  1117. return ch;
  1118. }
  1119.  
  1120.  
  1121. /****************************************************************************/
  1122. /*    FUNCTION:    Paginate                                                    */
  1123. /*                                                                            */
  1124. /*    PURPOSE:    Display article one page at a time.                            */
  1125. /*                                                                            */
  1126. /*    INPUT PARAMETERS:                                                        */
  1127. /*        NAME        I/O        DESCRIPTION                                        */
  1128. /*        ----        ---        -----------                                        */
  1129. /*        article         I        Article to be displayed.                        */
  1130. /*        lines         I        Lines already used on display.                    */
  1131. /*                                                                            */
  1132. /*    RETURNS:                                                                */
  1133. /*        Last command character hit by user if it needs to be processed        */
  1134. /*        by caller.                                                            */
  1135. /*                                                                            */
  1136. /*    COMMENTS:                                                                */
  1137. /*                                                                            */
  1138. /*    HISTORY:                                                                */
  1139. /*        1.    28 Sep 89        Created.                                        */
  1140. /*        2.    30 Dec 89        Return command letter hit at end of article.    */
  1141. /*        3.    12 Sep 90        Added parm for lines already printed on display.*/
  1142. /*        4.    24 Oct 90        Added percentage read display.                    */
  1143. /*        5.    14 Nov 90        Added CR/NL to print one more line.                */
  1144. /*        6.    22 Nov 90        End page at form feed.                            */
  1145. /*        7.    25 Nov 90        Added noscroll.  If set in config file, screen    */
  1146. /*                            will be cleared for each new page rather than    */
  1147. /*                            scrolling.                                        */
  1148. /*        8.    05 Jan 91        Fixed line-wrap calculation.                    */
  1149. /*        9.    05 Jan 91        Put last line of previous page at top of next    */
  1150. /*                            page in NO_SCROLL mode (for continuity).        */
  1151. /*       10.    06 Jan 91        Added "b" - go back one page.                    */
  1152. /*       11.  06 Jan 91        Added "a" - About BARN.                            */
  1153. /*                                                                            */
  1154. /****************************************************************************/
  1155.  
  1156. # define    PAGE_CMDS    " abFfjKkmnqRrw"
  1157. # define    KICK_CMDS    "kKwRrFfmn"        /* get kicked up to calling function */
  1158. static char *PAGE_HELP[] = {
  1159. "<space>   View next page of article.",
  1160. "<return>  Next line.",
  1161. "   a      About BARN.",
  1162. " b,<bs>   Go back one page.",
  1163. "  F,f     Followup, 'F' includes current article.",
  1164. " j,n,q    Quit the pager, marking the article as read.",
  1165. "   K      Same as 'k', and adds Subject to KILL file.",
  1166. "   k      Kill (mark as read) all articles w/this Subject.",
  1167. "   m      Mark as unread.",
  1168. "  R,r     Reply via mail, 'R' includes current article.",
  1169. "   w      Write the article to disk.  Prompts for a filename.",
  1170. (char *) NULL
  1171. };
  1172.  
  1173.  
  1174. Paginate( article, lines )
  1175.  
  1176. ARTICLE_INFO    *article;
  1177. int                lines;
  1178.  
  1179. {
  1180. char        filename[MAXLINE], buf[BUFSIZ], lastbuf[BUFSIZ], *readstatus;
  1181. FILE        *fp;
  1182. int            line_number;        /* what line of display the cursor is on */
  1183. int            cmd, rc = ' ';
  1184. int            length;
  1185. char        *p;
  1186.  
  1187. printf( "\r%*s\r", Columns, " " );
  1188. sprintf( filename, "%ld", article->number );
  1189. if ( ( fp = fopen( filename, "r" ) ) == NULLP( FILE ) )
  1190.     printf( "ARTICLE UNREADABLE!\n" );
  1191. else
  1192.     {
  1193.     fseek( fp, 0L, 2 );
  1194.     length = ftell( fp );
  1195.     fseek( fp, article->textpos, 0 );
  1196.     line_number = lines;
  1197.     while ( ( readstatus = fgets( buf, BUFSIZ, fp ) ) != NULLP( char ) )
  1198.         {
  1199.         if ( buf[strlen(buf)-1] == '\n' )    /* remove trailing newline */
  1200.              buf[strlen(buf)-1] = NULL;
  1201.         line_number += 1 + ( strlen( buf ) - 1 ) / Columns;
  1202.         if ( line_number > ( Lines - 1 ) || strchr( buf, '\f' ) != NULL )
  1203.             {
  1204.             /* inverse video please */
  1205.             INVERSE_ON;
  1206.             printf( "-- more (%d%%) --", (int) ((ftell( fp ) * 100) / length) );
  1207.             cmd = getcmd( PAGE_CMDS, PAGE_HELP, "\r\n\b" );
  1208.             INVERSE_OFF;
  1209.             printf( "\r%40s\r", " " );
  1210.             if ( ( p = strchr( buf, '\f' ) ) != NULL )
  1211.                 *p = ' ';
  1212.             if ( cmd == ' ' )
  1213.                 {
  1214.                 line_number = 0;
  1215.                 if ( GetVar( VAR_NOSCROLL ) != NULL )
  1216.                     {
  1217.                     Clear_Screen;
  1218.                     printf( "%s\n", lastbuf );
  1219.                     line_number += 1 + ( strlen( lastbuf ) - 1 ) / Columns;
  1220.                     }
  1221.                 line_number += 1 + ( strlen( buf ) - 1 ) / Columns;
  1222.                 printf( "%s\n", buf );
  1223.                 }
  1224.             else if ( cmd == '\n' || cmd == '\r' )
  1225.                 /*
  1226.                  *    Print one line.  Don't adjust line_number because we
  1227.                  *    want to prompt again for next line.
  1228.                  */
  1229.                 printf( "%s\n", buf );
  1230.             else if ( cmd == 'b' || cmd == '\b' )
  1231.                 {
  1232.                 long    currentpos, newpos;
  1233.                 char    *scratch, *ptr;
  1234.                 int        size, lines;
  1235.  
  1236.                 /* go back one page */
  1237.                 line_number = 0;
  1238.                 currentpos = ftell( fp ) - strlen( buf ) - 1;
  1239.                 newpos = currentpos - ( Lines * 2 ) * ( Columns + 1 );
  1240.                 if ( newpos < article -> textpos )
  1241.                     newpos = article -> textpos;
  1242.                 fseek( fp, newpos, 0 );
  1243.                 size = ( currentpos - ftell( fp ) ) * sizeof( char );
  1244.                 scratch = malloc( size );
  1245.                 (void) fread( scratch, sizeof( char ), size, fp );
  1246.                 ptr = scratch + size - 1;
  1247.                 lines = 0;
  1248.                 while ( ptr > scratch && lines < ( Lines - 1 ) * 2 )
  1249.                     {
  1250.                     if ( *ptr == '\n' )
  1251.                         ++lines;
  1252.                     --ptr;
  1253.                     }
  1254.                 if ( ptr > scratch )
  1255.                     newpos += ( ptr + 2 - scratch );
  1256.                 fseek( fp, newpos, 0 );
  1257.                 free( scratch );
  1258.                 if ( GetVar( VAR_NOSCROLL ) != NULL )
  1259.                     {
  1260.                     Clear_Screen;
  1261.                     }
  1262.                 }
  1263.             else if ( cmd == 'a' )
  1264.                 {
  1265.                 AboutBARN();
  1266.                 /* 
  1267.                  *    Fool this fn into re-reading the same line and printing
  1268.                  *    the prompt again.
  1269.                  */
  1270.                 fseek( fp, 0 - ( strlen( buf ) + 1 ), 1 );
  1271.                 strcpy( buf, lastbuf );
  1272.                 }
  1273.             else 
  1274.                 {
  1275.                 if ( strchr( KICK_CMDS, (char) cmd ) != NULL )
  1276.                     rc = cmd;    /* these commands get kicked up to caller */
  1277.                 break;
  1278.                 }
  1279.             }
  1280.         else
  1281.             printf( "%s\n", buf );
  1282.         strcpy( lastbuf, buf );
  1283.         }
  1284.     fclose( fp );
  1285.     if ( readstatus == NULLP( char ) )
  1286.         {
  1287.         INVERSE_ON;
  1288.         printf( "End of article %ld. ", article->number );
  1289.         cmd = getcmd( PAGE_CMDS, PAGE_HELP, "" );
  1290.         INVERSE_OFF;
  1291.         if ( strchr( KICK_CMDS, (char) cmd ) != NULL )
  1292.             rc = cmd;    /* these commands get kicked up to caller */
  1293.         }
  1294.     }
  1295. return rc;
  1296. }
  1297.  
  1298.  
  1299. /****************************************************************************/
  1300. /*    FUNCTION:    MakePattern                                                    */
  1301. /*                                                                            */
  1302. /*    PURPOSE:    Attempt to create a valid regular expression pattern.        */
  1303. /*                                                                            */
  1304. /*    INPUT PARAMETERS:                                                        */
  1305. /*        NAME        I/O        DESCRIPTION                                        */
  1306. /*        ----        ---        -----------                                        */
  1307. /*        pattern         O        Put string form of pattern here.                */
  1308. /*        text         I        Text to be matched.                                */
  1309. /*                                                                            */
  1310. /*    RETURNS:                                                                */
  1311. /*        (struct regexp *)    Regular expression internal form.                */
  1312. /*        (NULL)                Couldn't grok pattern.                            */
  1313. /*                                                                            */
  1314. /*    COMMENTS:                                                                */
  1315. /*        Finds characters with special meaning to regexp and escapes them by    */
  1316. /*        preceding them with a backslash.                                    */
  1317. /*                                                                            */
  1318. /*        Pattern had better be large enough to hold the resulting string.    */
  1319. /*                                                                            */
  1320. /*    HISTORY:                                                                */
  1321. /*        1.    22 Oct 90        Created.                                        */
  1322. /*        2.    28 Nov 90        Added backslash as char that needs to be        */
  1323. /*                            backslashed.                                    */
  1324. /*                            Match random text between optional 'Re:' stuff    */
  1325. /*                            and 'text' pattern.                                */
  1326. /*                                                                            */
  1327. /****************************************************************************/
  1328.  
  1329. struct regexp *MakePattern( pattern, text )
  1330.  
  1331. char                *pattern;
  1332. char                *text;
  1333.  
  1334. {
  1335. char                temp[BUFSIZ];    /* translated string pattern */
  1336. char                base[BUFSIZ];    /* string without Re: stuff */
  1337. char                *p, *pbase;
  1338. struct regexp        *remover_re;
  1339. struct regexp        *re;            /* resulting regular expression */
  1340. int                    len;
  1341. # ifdef DEBUG_REGSUB
  1342. int                    i;
  1343. # endif
  1344.  
  1345. strcpy( base, "\\1" );
  1346. if ( ( remover_re = regcomp( REPLY_REMOVER ) ) == NULL )
  1347.     fprintf( stderr, "Can't compile REPLY_REMOVER\n" );
  1348. # ifdef DEBUG_REGSUB
  1349. if ( regexec( remover_re, text ) == 1 )
  1350.     for ( i = 0; i < 10; i++ )
  1351.         printf( "%d '%*s'\n", i, remover_re -> endp[1] - remover_re -> startp[1], 
  1352.           remover_re -> startp[1] );
  1353. regsub( remover_re, text, base );
  1354. printf( "but base is '%s'\n", base );
  1355. getchar();
  1356. # else
  1357. (void) regexec( remover_re, text );
  1358. len = remover_re -> endp[1] - remover_re -> startp[1];
  1359. strncpy( base, remover_re -> startp[1], len );
  1360. base[len] = NULL;
  1361. # endif
  1362. p = temp; pbase = base;
  1363. while ( *pbase != NULL )
  1364.     {
  1365.     switch ( *pbase )
  1366.         {
  1367.         case '.':
  1368.         case '*':
  1369.         case '+':
  1370.         case '(':
  1371.         case ')':
  1372.         case '[':
  1373.         case ']':
  1374.         case '^':
  1375.         case '$':
  1376.         case '-':
  1377.         case '?':
  1378.         case '|':
  1379.         case '\\':
  1380.                     *p++ = '\\';
  1381.         default:
  1382.                     *p++ = *pbase;
  1383.                     break;
  1384.         }
  1385.     pbase++;
  1386.     }
  1387. *p = NULL;
  1388. /*
  1389.  *    Try to match the Re: stuff that followups generate.
  1390.  */
  1391. strcpy( pattern, REPLY_PATTERN );
  1392. strcat( pattern, ".*" );
  1393. strcat( pattern, temp );
  1394. re = regcomp( pattern );
  1395. if ( remover_re != NULL )
  1396.     free( (char *) remover_re );
  1397. return re;
  1398. }
  1399.  
  1400.  
  1401. /****************************************************************************/
  1402. /*    FUNCTION:    ScanSubjects                                                */
  1403. /*                                                                            */
  1404. /*    PURPOSE:    List articles in newsgroup by subject.                        */
  1405. /*                                                                            */
  1406. /*    INPUT PARAMETERS:                                                        */
  1407. /*        NAME        I/O        DESCRIPTION                                        */
  1408. /*        ----        ---        -----------                                        */
  1409. /*        arts         I        Header of list of articles in newsgroup.        */
  1410. /*                                                                            */
  1411. /*    RETURNS:                                                                */
  1412. /*                                                                            */
  1413. /*    COMMENTS:                                                                */
  1414. /*                                                                            */
  1415. /*    HISTORY:                                                                */
  1416. /*        1.    24 Oct 90        Created.                                        */
  1417. /*        2.    14 Nov 90        Added CR to print one more line.                */
  1418. /*        3.    16 Nov 90        Bug fix: always showed first article even if    */
  1419. /*                            it had already been read.                        */
  1420. /*                                                                            */
  1421. /****************************************************************************/
  1422.  
  1423. # define    SCAN_CMDS        "q "
  1424. static char *SCAN_HELP[] = {
  1425. "<space>   View next page of subjects.",
  1426. "<return>  View one more line.",
  1427. "   q      Quit scanning subjects.",
  1428. (char *) NULL
  1429. };
  1430.  
  1431. void ScanSubjects( arts )
  1432.  
  1433. ARTICLE_INFO        *arts;
  1434.  
  1435. {
  1436. ARTICLE_INFO        *pbase;            /* ptr to base article of subject thread */
  1437. ARTICLE_INFO        *preply;        /* tmep ptr to reply articles */
  1438. int                    n_replies;        /* # of replies to a base article */
  1439. char                *sub;            /* subject */
  1440. char                dummy[MAXLINE];    /* not used */
  1441. struct regexp        *pat;            /* pattern to match subjects */
  1442. int                    printed = 0;    /* # subjects printed this screenful */
  1443. int                    cmd;
  1444.  
  1445. printf( "\n" );
  1446. for ( pbase = arts; pbase != NULL; pbase = pbase -> next )
  1447.     if ( ! pbase -> beenread )
  1448.         pbase -> done = FALSE;
  1449.     else
  1450.         pbase -> done = TRUE;
  1451. /*
  1452.  *    Find first unread base article.
  1453.  */
  1454. for ( pbase = arts; pbase != NULL && pbase -> done; pbase = pbase -> next ) ;
  1455. while ( pbase != NULL )
  1456.     {
  1457.     if ( pbase -> subject )
  1458.         sub = strdup( pbase -> subject );
  1459.     else
  1460.         sub = strdup( "<None>" );
  1461.     pbase -> done = TRUE;
  1462.     /*
  1463.      *    Count replies.
  1464.      */
  1465.     n_replies = 0;
  1466.     pat = MakePattern( dummy, sub );
  1467.     for ( preply = pbase -> next; preply != NULL; preply = preply -> next )
  1468.         if ( ! preply -> done && preply -> subject && 
  1469.           regexec( pat, preply -> subject ) == 1 )
  1470.             {
  1471.             n_replies++;
  1472.             preply -> done = TRUE;
  1473.             }
  1474.     printf( "%5ld: (%2d replies) %-55.55s\n", pbase -> number, n_replies, sub );
  1475.     if ( pat != NULL )
  1476.         free( (char *) pat );
  1477.     if ( sub != NULL )
  1478.         free( sub );
  1479.     /*
  1480.      *    Find next base article we haven't processed.
  1481.      */
  1482.     for ( pbase = pbase -> next; pbase != NULL && pbase -> done;
  1483.       pbase = pbase -> next )
  1484.           ;
  1485.     if ( ++printed >= Lines - 1 )
  1486.         {
  1487.         /* inverse video please */
  1488.         INVERSE_ON;
  1489.         printf( "--more--" );
  1490.         cmd = getcmd( SCAN_CMDS, SCAN_HELP, "\r\n" );
  1491.         INVERSE_OFF;
  1492.         printf( "\r%40s\r", " " );
  1493.         if ( cmd != '\n' && cmd != '\r' )
  1494.             printed = 0;
  1495.         if ( cmd == 'q' )
  1496.             break;
  1497.         }
  1498.     }
  1499. if ( printed > 0 )
  1500.     {
  1501.     /* inverse video please */
  1502.     INVERSE_ON;
  1503.     printf( "--more--" );
  1504.     (void) getcmd( SCAN_CMDS, SCAN_HELP, "" );
  1505.     INVERSE_OFF;
  1506.     printf( "\r%40s\r", " " );
  1507.     }
  1508. for ( pbase = arts; pbase != NULL; pbase = pbase -> next )
  1509.     if ( ! pbase -> beenread )
  1510.         pbase -> done = FALSE;
  1511.     else
  1512.         pbase -> done = TRUE;
  1513. }
  1514.